Python & Wearables

FrOSCon 10, 22.08.2015, St. Augustin

Python Devroom

Andreas Schreiber

Deutsches Zentrum für Luft- und Raumfahrt e.V.

Twitter @onyame

Web andreas-schreiber.net

Email andreas.schreiber@dlr.de

Alexander Nguyen

Deutsches Zentrum für Luft- und Raumfahrt e.V.

Twitter @a8t5n

Email alex.nguyen@t-online.de

Simon Siegert

Deutsches Zentrum für Luft- und Raumfahrt e.V.

Email simon.siegert@gmx.de

Inhalt

  • Wearables
  • Wearables um uns herum...
  • Fitbit Schrittzähler
  • Vergleich zweier Schrittzähler
  • Muse Headband EEG

Wearables

Wearables: Tragbare Computersysteme

  • Am Körper befestigt
  • Enthalten Sensoren
  • Datenübertragung

Verwandte Technologien

  • Ubiquitious Computing (allgegenwärtige Computer)
  • Pervasive Computing und Internet of Things
  • Mensch-Maschine-Interaktion

Hörgerät

Weitere Beispiele für Wearables

  • Schrittzähler
  • Pulsuhren
  • Fitnessarmbänder
  • Herzfrequenzmesser (Brustgurt)
  • Stirnbänder (EEG)
  • Smart Watches
  • Smart Glasses
  • Smart Clothing
  • Smart Phones

Wearables um uns herum...

Wearables in unserer Umgebung

Devices mit Bluetooth

  • Bluetooth Low Energy, Bluetooth LE, BLE

Smartphone als BLE Scanner

  • Android App RaMBLE - Bluetooth LE Mapper
  • RaMBLE (Google Play Store)

RaMBLE

  • RaMBLE bieter Export der Daten als SQLite-Datenbank
  • Öffnen der Datenbank mit Python

In [2]:
import sqlite3
db = sqlite3.connect("btle_backup_20082015_2118.sqlite")

Einlesen in ein Pandas DataFrame


In [3]:
import pandas as pd

df = pd.read_sql("SELECT * from devices", db,index_col='id')

BLE Devices als Tabelle


In [135]:
df.tail()


Out[135]:
address addresstype devicename devicetype firstseen lastseen scanresp manufacturerspecificdatakey manufacturerspecificdatafield servicesuuid16 servicesuuid32 servicesuuid128 solicitedservicesuuid16 solicitedservicesuuid128 servicedata
id
2854 EA:9B:4B:79:34:31 None Surge Fitbit Charge HR (from ServiceUUID) 03/08/2015 16:41:38 03/08/2015 16:41:38 None NaN None None None adabfb006e7d4601bda2bffaa68956ba None None Device Information (180a): 1006c85903
2855 41:BC:CA:C5:16:35 None None Apple device (from MSData) 03/08/2015 16:42:06 03/08/2015 16:42:06 None 76 05120000000000000000018ba83d040000000000 None None None None None None
2856 DB:57:4F:AB:02:88 None Charge HR Fitbit Charge HR (from device name), Fitbit Ch... 03/08/2015 16:42:51 03/08/2015 16:42:51 None NaN None None None adabfb006e7d4601bda2bffaa68956ba None None Device Information (180a): 1204c7410000
2857 7F:46:0C:BE:51:2B None None Apple device (from MSData) 03/08/2015 16:42:50 03/08/2015 16:42:50 None 76 0c0e002031fcf49234b7e4c1e62d157d None None None None None None
2858 7F:F4:93:34:A8:EE None None Apple device (from MSData) 03/08/2015 16:44:00 03/08/2015 16:44:00 None 76 0c0e00bca1894581f4345a1b722a5a9b None None None None None None

Wir interessieren uns für die Device-Typen...

Ein wenig Data Cleaning

  • Die überflüssigen Klammern mit Inhalt "(from MSData)" entfernen

In [136]:
df['devicetype'] = df['devicetype'].str.replace('\(.*\)', '', case=False)

Gruppieren und zählen

  • Pandas bietet eine sehr leistungsfähige Funktion zum Aggregieren von Daten: groupby

In [137]:
device_types = df.groupby('devicetype').size()

In [138]:
device_types


Out[138]:
devicetype
Apple device              2077
Chipolo tracker              1
Ericsson device              2
Fitbit Charge               36
Fitbit Charge HR            78
Fitbit Flex                 49
Fitbit One                  63
Garmin Device               20
Garmin Vivosmart             3
Google device                1
HTC RECAM                    2
Jawbone UP24                17
Jawbone device               1
MI                          12
Misfit Wearables Corp       11
Nike device                 11
Nordic device                1
Parrot MINIKIT               1
Samsung device             137
Unknown 0x35EE device        6
Unknown device             307
Withings device             22
dtype: int64

Darstellen als Bar Chart


In [139]:
import seaborn as sns
device_types.plot(kind='bar')


Out[139]:
<matplotlib.axes._subplots.AxesSubplot at 0x11a945f90>

Übersichtlicher machen

  • Wir entfernen einige Device-Typen
    • 'Apple device'
    • 'Samsung device'
    • 'Unknown device'

In [140]:
devices = device_types.drop([u'Apple device ', u'Samsung device ', u'Unknown device'])

Plot der restlichen Devices...


In [141]:
devices.plot(kind='bar')


Out[141]:
<matplotlib.axes._subplots.AxesSubplot at 0x112f4bdd0>

Fitbit Schrittzähler

Fitbit

  • Activity tracker
    • Schritte
    • Stockwerke
    • Schlaf
  • Verschiedene Modelle
    • Fitbit One
    • ...

Fitbit API

Client Keys und OAuth Credentials

Best Practice: Keys und Credentials in Python


In [3]:
import fitbit_user

fitbit_user.py

user_id = '2****M'
access_token_secret = '7******************************9'
access_token = 'b******************************0'
consumer_key_steps = 'd******************************0'
consumer_secret_steps = '5******************************2'

Python-Package für das Fitbit API


In [2]:
from fitbit import Fitbit

Authentifizieren und Autorisieren der Applikation

  • Authentifizieren mit OAuth 1

In [15]:
from fitbit import FitbitOauthClient
client = FitbitOauthClient(fitbit_user.consumer_key_steps, fitbit_user.consumer_secret_steps)
request_token = client.fetch_request_token()
print request_token


{u'oauth_token_secret': u'f****************************f', u'oauth_token': u'f****************************d', u'oauth_callback_confirmed': u'true'}

In [16]:
import webbrowser
webbrowser.open(client.authorize_token_url())


Out[16]:
True

OAuth Access Token holen


In [17]:
token = client.fetch_access_token('6****************************0', request_token)
print(token)
user_token = token["oauth_token"]
user_secret = token["oauth_token_secret"]
user_id = token["encoded_user_id"]


{u'oauth_token_secret': u'5****************************1', u'encoded_user_id': u'22X4YM', u'oauth_token': u'b****************************7'}

Initialisieren eines authentifizierten Fitbit-Clients


In [30]:
fitbit_client = Fitbit(fitbit_user.consumer_key_steps, 
                       fitbit_user.consumer_secret_steps, 
                       resource_owner_key = user_token,
                       resource_owner_secret = user_secret,
                       user_id=user_id)

Holen des Benutzerprofils


In [31]:
user_profile = fitbit_client.user_profile_get(user_id=user_id)

In [32]:
user_profile


Out[32]:
{u'user': {u'age': 45,
  u'avatar': u'https://d6y8zfzc2qfsl.cloudfront.net/8A3C66FA-61D2-5D41-97D9-A1140AFD7B0C_profile_100_square.png',
  u'avatar150': u'https://d6y8zfzc2qfsl.cloudfront.net/8A3C66FA-61D2-5D41-97D9-A1140AFD7B0C_profile_150_square.png',
  u'averageDailySteps': 12944,
  u'country': u'DE',
  u'dateOfBirth': u'1970-06-08',
  u'displayName': u'Andreas',
  u'distanceUnit': u'METRIC',
  u'encodedId': u'22X4YM',
  u'foodsLocale': u'de_DE',
  u'fullName': u'Andreas Schreiber',
  u'gender': u'MALE',
  u'glucoseUnit': u'METRIC',
  u'height': 69.68503937007874,
  u'heightUnit': u'METRIC',
  u'locale': u'de_DE',
  u'memberSince': u'2012-05-26',
  u'offsetFromUTCMillis': -21600000,
  u'startDayOfWeek': u'MONDAY',
  u'strideLengthRunning': 36.25984251968504,
  u'strideLengthWalking': 28.937007874015748,
  u'timezone': u'America/Edmonton',
  u'topBadges': [{u'badgeGradientEndColor': u'FF677C',
    u'badgeGradientStartColor': u'D24958',
    u'badgeType': u'DAILY_STEPS',
    u'category': u'T\xe4gliche Schritte',
    u'cheers': [],
    u'dateTime': u'2015-07-30',
    u'description': u'35.000 Schritte an einem Tag',
    u'earnedMessage': u'Gl\xfcckwunsch zu deinem ersten Bergstiefel-Abzeichen!',
    u'encodedId': u'228TPW',
    u'image100px': u'http://static0.fitbit.com/images/badges_new/100px/badge_daily_steps35k.png',
    u'image125px': u'http://static0.fitbit.com/images/badges_new/125px/badge_daily_steps35k.png',
    u'image300px': u'http://static0.fitbit.com/images/badges_new/300px/badge_daily_steps35k.png',
    u'image50px': u'http://static0.fitbit.com/images/badges_new/badge_daily_steps35k.png',
    u'image75px': u'http://static0.fitbit.com/images/badges_new/75px/badge_daily_steps35k.png',
    u'marketingDescription': u'Du hast 35.000\xa0Schritte zur\xfcckgelegt und dir damit das Bergstiefel-Abzeichen verdient.',
    u'mobileDescription': u'Sauber! F\xfcr dich ist kein Gipfel zu hoch und kein Ziel unerreichbar.',
    u'name': u'Bergstiefel (35.000 Schritte an einem Tag)',
    u'shareImage640px': u'http://static0.fitbit.com/images/badges_new/386px/shareLocalized/de_DE/badge_daily_steps35k.png',
    u'shareText': u'I took 35.000 steps and earned the Bergstiefel badge! #Fitbit',
    u'shortDescription': u'35.000 Schritte',
    u'shortName': u'Bergstiefel',
    u'timesAchieved': 1,
    u'value': 35000},
   {u'badgeGradientEndColor': u'42C401',
    u'badgeGradientStartColor': u'007D3C',
    u'badgeType': u'LIFETIME_DISTANCE',
    u'category': u'Gesamtstrecke',
    u'cheers': [],
    u'dateTime': u'2015-06-16',
    u'description': u'8.046 km insgesamt',
    u'earnedMessage': u'Super! Du hast dir das Afrika-Abzeichen verdient.',
    u'encodedId': u'22B8M4',
    u'image100px': u'http://static0.fitbit.com/images/badges_new/100px/badge_lifetime_miles5000.png',
    u'image125px': u'http://static0.fitbit.com/images/badges_new/125px/badge_lifetime_miles5000.png',
    u'image300px': u'http://static0.fitbit.com/images/badges_new/300px/badge_lifetime_miles5000.png',
    u'image50px': u'http://static0.fitbit.com/images/badges_new/badge_lifetime_miles5000.png',
    u'image75px': u'http://static0.fitbit.com/images/badges_new/75px/badge_lifetime_miles5000.png',
    u'marketingDescription': u'Indem du insgesamt 8.046 kilometers gegangen bist, hast du dir das Afrika-Abzeichen verdient.',
    u'mobileDescription': u'Du hast die gesamte L\xe4nge des afrikanischen Kontinents abgeschritten. Da machen nicht nur die Giraffen lange H\xe4lse!',
    u'name': u'Afrika (8.046 km insgesamt)',
    u'shareImage640px': u'http://static0.fitbit.com/images/badges_new/386px/shareLocalized/de_DE/badge_lifetime_miles5000_km.png',
    u'shareText': u'I covered 8.046 kilometers with my #Fitbit and earned the Afrika badge.',
    u'shortDescription': u'8.046 Kilometer',
    u'shortName': u'Afrika',
    u'timesAchieved': 1,
    u'unit': u'KILOMETERS',
    u'value': 8046},
   {u'badgeGradientEndColor': u'FFDB01',
    u'badgeGradientStartColor': u'D99123',
    u'badgeType': u'DAILY_FLOORS',
    u'category': u'T\xe4gliche Etagen',
    u'cheers': [],
    u'dateTime': u'2015-07-30',
    u'description': u'300 Etagen an einem Tag',
    u'earnedMessage': u'Gl\xfcckwunsch zu deinem ersten Wasserfall-Abzeichen!',
    u'encodedId': u'22984B',
    u'image100px': u'http://static0.fitbit.com/images/badges_new/100px/badge_daily_floors300.png',
    u'image125px': u'http://static0.fitbit.com/images/badges_new/125px/badge_daily_floors300.png',
    u'image300px': u'http://static0.fitbit.com/images/badges_new/300px/badge_daily_floors300.png',
    u'image50px': u'http://static0.fitbit.com/images/badges_new/badge_daily_floors300.png',
    u'image75px': u'http://static0.fitbit.com/images/badges_new/75px/badge_daily_floors300.png',
    u'marketingDescription': u'Mit 300\xa0Etagen hast du dir das Wasserfall-Abzeichen verdient.',
    u'mobileDescription': u'Es scheint, als w\xe4rst du wirklich \xfcber dich hinaus gewachsen!',
    u'name': u'Wasserfall (300 Etagen an einem Tag)',
    u'shareImage640px': u'http://static0.fitbit.com/images/badges_new/386px/shareLocalized/de_DE/badge_daily_floors300.png',
    u'shareText': u'I climbed 300 flights of stairs and earned the Wasserfall badge! #Fitbit',
    u'shortDescription': u'300 Etagen',
    u'shortName': u'Wasserfall',
    u'timesAchieved': 1,
    u'value': 300},
   {u'badgeGradientEndColor': u'00D3D6',
    u'badgeGradientStartColor': u'007273',
    u'badgeType': u'LIFETIME_FLOORS',
    u'category': u'Gesamtetagen',
    u'cheers': [],
    u'dateTime': u'2015-03-04',
    u'description': u'14.000 Etagen insgesamt',
    u'earnedMessage': u'Juhu! Du hast dir das Raumschiff-Abzeichen verdient.',
    u'encodedId': u'228TKC',
    u'image100px': u'http://static0.fitbit.com/images/badges_new/100px/badge_lifetime_floors14k.png',
    u'image125px': u'http://static0.fitbit.com/images/badges_new/125px/badge_lifetime_floors14k.png',
    u'image300px': u'http://static0.fitbit.com/images/badges_new/300px/badge_lifetime_floors14k.png',
    u'image50px': u'http://static0.fitbit.com/images/badges_new/badge_lifetime_floors14k.png',
    u'image75px': u'http://static0.fitbit.com/images/badges_new/75px/badge_lifetime_floors14k.png',
    u'marketingDescription': u'Du hast insgesamt 14.000\xa0Etagen erklommen und dir damit das Raumschiff-Abzeichen verdient.',
    u'mobileDescription': u'Richtig abgehoben! Mit dieser Leistung bist du wirklich nicht von dieser Welt. Du bist ein wahrer Aufsteiger.',
    u'name': u'Raumschiff (14.000 Etagen insgesamt)',
    u'shareImage640px': u'http://static0.fitbit.com/images/badges_new/386px/shareLocalized/de_DE/badge_lifetime_floors14k.png',
    u'shareText': u'I climbed 14.000 floors with my #Fitbit and earned the Raumschiff badge.',
    u'shortDescription': u'14.000 Etagen',
    u'shortName': u'Raumschiff',
    u'timesAchieved': 1,
    u'value': 14000}],
  u'waterUnit': u'METRIC',
  u'waterUnitName': u'ml',
  u'weight': 186.2,
  u'weightUnit': u'METRIC'}}

Werte von Fitbit holen

Holen der Schritte eines Jahres


In [35]:
activities_steps = fitbit_client.time_series('activities/steps', user_id=user_id, period='1y')

In [36]:
activities_steps


Out[36]:
{u'activities-steps': [{u'dateTime': u'2014-08-22', u'value': u'12673'},
  {u'dateTime': u'2014-08-23', u'value': u'11408'},
  {u'dateTime': u'2014-08-24', u'value': u'11585'},
  {u'dateTime': u'2014-08-25', u'value': u'16252'},
  {u'dateTime': u'2014-08-26', u'value': u'12152'},
  {u'dateTime': u'2014-08-27', u'value': u'6309'},
  {u'dateTime': u'2014-08-28', u'value': u'10156'},
  {u'dateTime': u'2014-08-29', u'value': u'8732'},
  {u'dateTime': u'2014-08-30', u'value': u'10810'},
  {u'dateTime': u'2014-08-31', u'value': u'3325'},
  {u'dateTime': u'2014-09-01', u'value': u'11830'},
  {u'dateTime': u'2014-09-02', u'value': u'7'},
  {u'dateTime': u'2014-09-03', u'value': u'9995'},
  {u'dateTime': u'2014-09-04', u'value': u'6651'},
  {u'dateTime': u'2014-09-05', u'value': u'10855'},
  {u'dateTime': u'2014-09-06', u'value': u'6759'},
  {u'dateTime': u'2014-09-07', u'value': u'11321'},
  {u'dateTime': u'2014-09-08', u'value': u'13040'},
  {u'dateTime': u'2014-09-09', u'value': u'8958'},
  {u'dateTime': u'2014-09-10', u'value': u'11548'},
  {u'dateTime': u'2014-09-11', u'value': u'10912'},
  {u'dateTime': u'2014-09-12', u'value': u'11327'},
  {u'dateTime': u'2014-09-13', u'value': u'7225'},
  {u'dateTime': u'2014-09-14', u'value': u'4398'},
  {u'dateTime': u'2014-09-15', u'value': u'0'},
  {u'dateTime': u'2014-09-16', u'value': u'10849'},
  {u'dateTime': u'2014-09-17', u'value': u'11318'},
  {u'dateTime': u'2014-09-18', u'value': u'13524'},
  {u'dateTime': u'2014-09-19', u'value': u'8781'},
  {u'dateTime': u'2014-09-20', u'value': u'16795'},
  {u'dateTime': u'2014-09-21', u'value': u'3519'},
  {u'dateTime': u'2014-09-22', u'value': u'12446'},
  {u'dateTime': u'2014-09-23', u'value': u'16350'},
  {u'dateTime': u'2014-09-24', u'value': u'8168'},
  {u'dateTime': u'2014-09-25', u'value': u'9819'},
  {u'dateTime': u'2014-09-26', u'value': u'11047'},
  {u'dateTime': u'2014-09-27', u'value': u'7605'},
  {u'dateTime': u'2014-09-28', u'value': u'7272'},
  {u'dateTime': u'2014-09-29', u'value': u'6933'},
  {u'dateTime': u'2014-09-30', u'value': u'13630'},
  {u'dateTime': u'2014-10-01', u'value': u'15471'},
  {u'dateTime': u'2014-10-02', u'value': u'9044'},
  {u'dateTime': u'2014-10-03', u'value': u'7033'},
  {u'dateTime': u'2014-10-04', u'value': u'9925'},
  {u'dateTime': u'2014-10-05', u'value': u'5635'},
  {u'dateTime': u'2014-10-06', u'value': u'11758'},
  {u'dateTime': u'2014-10-07', u'value': u'8552'},
  {u'dateTime': u'2014-10-08', u'value': u'9946'},
  {u'dateTime': u'2014-10-09', u'value': u'10167'},
  {u'dateTime': u'2014-10-10', u'value': u'13919'},
  {u'dateTime': u'2014-10-11', u'value': u'11488'},
  {u'dateTime': u'2014-10-12', u'value': u'6037'},
  {u'dateTime': u'2014-10-13', u'value': u'9079'},
  {u'dateTime': u'2014-10-14', u'value': u'13104'},
  {u'dateTime': u'2014-10-15', u'value': u'10202'},
  {u'dateTime': u'2014-10-16', u'value': u'9246'},
  {u'dateTime': u'2014-10-17', u'value': u'15609'},
  {u'dateTime': u'2014-10-18', u'value': u'7524'},
  {u'dateTime': u'2014-10-19', u'value': u'5416'},
  {u'dateTime': u'2014-10-20', u'value': u'13681'},
  {u'dateTime': u'2014-10-21', u'value': u'10099'},
  {u'dateTime': u'2014-10-22', u'value': u'9711'},
  {u'dateTime': u'2014-10-23', u'value': u'16379'},
  {u'dateTime': u'2014-10-24', u'value': u'12416'},
  {u'dateTime': u'2014-10-25', u'value': u'9479'},
  {u'dateTime': u'2014-10-26', u'value': u'10415'},
  {u'dateTime': u'2014-10-27', u'value': u'10012'},
  {u'dateTime': u'2014-10-28', u'value': u'4628'},
  {u'dateTime': u'2014-10-29', u'value': u'6232'},
  {u'dateTime': u'2014-10-30', u'value': u'3779'},
  {u'dateTime': u'2014-10-31', u'value': u'9721'},
  {u'dateTime': u'2014-11-01', u'value': u'8852'},
  {u'dateTime': u'2014-11-02', u'value': u'11403'},
  {u'dateTime': u'2014-11-03', u'value': u'12157'},
  {u'dateTime': u'2014-11-04', u'value': u'11991'},
  {u'dateTime': u'2014-11-05', u'value': u'63'},
  {u'dateTime': u'2014-11-06', u'value': u'13445'},
  {u'dateTime': u'2014-11-07', u'value': u'9791'},
  {u'dateTime': u'2014-11-08', u'value': u'10818'},
  {u'dateTime': u'2014-11-09', u'value': u'5402'},
  {u'dateTime': u'2014-11-10', u'value': u'13441'},
  {u'dateTime': u'2014-11-11', u'value': u'6778'},
  {u'dateTime': u'2014-11-12', u'value': u'5094'},
  {u'dateTime': u'2014-11-13', u'value': u'6939'},
  {u'dateTime': u'2014-11-14', u'value': u'7753'},
  {u'dateTime': u'2014-11-15', u'value': u'16324'},
  {u'dateTime': u'2014-11-16', u'value': u'13120'},
  {u'dateTime': u'2014-11-17', u'value': u'14364'},
  {u'dateTime': u'2014-11-18', u'value': u'11893'},
  {u'dateTime': u'2014-11-19', u'value': u'17634'},
  {u'dateTime': u'2014-11-20', u'value': u'18821'},
  {u'dateTime': u'2014-11-21', u'value': u'10089'},
  {u'dateTime': u'2014-11-22', u'value': u'6570'},
  {u'dateTime': u'2014-11-23', u'value': u'12245'},
  {u'dateTime': u'2014-11-24', u'value': u'11145'},
  {u'dateTime': u'2014-11-25', u'value': u'12200'},
  {u'dateTime': u'2014-11-26', u'value': u'7721'},
  {u'dateTime': u'2014-11-27', u'value': u'0'},
  {u'dateTime': u'2014-11-28', u'value': u'0'},
  {u'dateTime': u'2014-11-29', u'value': u'7887'},
  {u'dateTime': u'2014-11-30', u'value': u'13330'},
  {u'dateTime': u'2014-12-01', u'value': u'10284'},
  {u'dateTime': u'2014-12-02', u'value': u'5636'},
  {u'dateTime': u'2014-12-03', u'value': u'0'},
  {u'dateTime': u'2014-12-04', u'value': u'8856'},
  {u'dateTime': u'2014-12-05', u'value': u'9414'},
  {u'dateTime': u'2014-12-06', u'value': u'5120'},
  {u'dateTime': u'2014-12-07', u'value': u'5642'},
  {u'dateTime': u'2014-12-08', u'value': u'11066'},
  {u'dateTime': u'2014-12-09', u'value': u'9488'},
  {u'dateTime': u'2014-12-10', u'value': u'4641'},
  {u'dateTime': u'2014-12-11', u'value': u'12767'},
  {u'dateTime': u'2014-12-12', u'value': u'17384'},
  {u'dateTime': u'2014-12-13', u'value': u'13977'},
  {u'dateTime': u'2014-12-14', u'value': u'21717'},
  {u'dateTime': u'2014-12-15', u'value': u'7389'},
  {u'dateTime': u'2014-12-16', u'value': u'13827'},
  {u'dateTime': u'2014-12-17', u'value': u'10891'},
  {u'dateTime': u'2014-12-18', u'value': u'23841'},
  {u'dateTime': u'2014-12-19', u'value': u'5879'},
  {u'dateTime': u'2014-12-20', u'value': u'5617'},
  {u'dateTime': u'2014-12-21', u'value': u'10421'},
  {u'dateTime': u'2014-12-22', u'value': u'15019'},
  {u'dateTime': u'2014-12-23', u'value': u'20148'},
  {u'dateTime': u'2014-12-24', u'value': u'28703'},
  {u'dateTime': u'2014-12-25', u'value': u'12738'},
  {u'dateTime': u'2014-12-26', u'value': u'16699'},
  {u'dateTime': u'2014-12-27', u'value': u'11235'},
  {u'dateTime': u'2014-12-28', u'value': u'0'},
  {u'dateTime': u'2014-12-29', u'value': u'0'},
  {u'dateTime': u'2014-12-30', u'value': u'14001'},
  {u'dateTime': u'2014-12-31', u'value': u'8740'},
  {u'dateTime': u'2015-01-01', u'value': u'6303'},
  {u'dateTime': u'2015-01-02', u'value': u'5025'},
  {u'dateTime': u'2015-01-03', u'value': u'2254'},
  {u'dateTime': u'2015-01-04', u'value': u'4805'},
  {u'dateTime': u'2015-01-05', u'value': u'8845'},
  {u'dateTime': u'2015-01-06', u'value': u'17768'},
  {u'dateTime': u'2015-01-07', u'value': u'17875'},
  {u'dateTime': u'2015-01-08', u'value': u'10327'},
  {u'dateTime': u'2015-01-09', u'value': u'9118'},
  {u'dateTime': u'2015-01-10', u'value': u'5538'},
  {u'dateTime': u'2015-01-11', u'value': u'10310'},
  {u'dateTime': u'2015-01-12', u'value': u'9999'},
  {u'dateTime': u'2015-01-13', u'value': u'9479'},
  {u'dateTime': u'2015-01-14', u'value': u'9904'},
  {u'dateTime': u'2015-01-15', u'value': u'9459'},
  {u'dateTime': u'2015-01-16', u'value': u'9918'},
  {u'dateTime': u'2015-01-17', u'value': u'5039'},
  {u'dateTime': u'2015-01-18', u'value': u'4596'},
  {u'dateTime': u'2015-01-19', u'value': u'8771'},
  {u'dateTime': u'2015-01-20', u'value': u'7570'},
  {u'dateTime': u'2015-01-21', u'value': u'8685'},
  {u'dateTime': u'2015-01-22', u'value': u'10857'},
  {u'dateTime': u'2015-01-23', u'value': u'13745'},
  {u'dateTime': u'2015-01-24', u'value': u'9491'},
  {u'dateTime': u'2015-01-25', u'value': u'3871'},
  {u'dateTime': u'2015-01-26', u'value': u'9965'},
  {u'dateTime': u'2015-01-27', u'value': u'8338'},
  {u'dateTime': u'2015-01-28', u'value': u'10578'},
  {u'dateTime': u'2015-01-29', u'value': u'5474'},
  {u'dateTime': u'2015-01-30', u'value': u'2952'},
  {u'dateTime': u'2015-01-31', u'value': u'1768'},
  {u'dateTime': u'2015-02-01', u'value': u'4613'},
  {u'dateTime': u'2015-02-02', u'value': u'8394'},
  {u'dateTime': u'2015-02-03', u'value': u'5429'},
  {u'dateTime': u'2015-02-04', u'value': u'11432'},
  {u'dateTime': u'2015-02-05', u'value': u'8312'},
  {u'dateTime': u'2015-02-06', u'value': u'14449'},
  {u'dateTime': u'2015-02-07', u'value': u'4982'},
  {u'dateTime': u'2015-02-08', u'value': u'8548'},
  {u'dateTime': u'2015-02-09', u'value': u'10466'},
  {u'dateTime': u'2015-02-10', u'value': u'9846'},
  {u'dateTime': u'2015-02-11', u'value': u'9672'},
  {u'dateTime': u'2015-02-12', u'value': u'13921'},
  {u'dateTime': u'2015-02-13', u'value': u'11059'},
  {u'dateTime': u'2015-02-14', u'value': u'8886'},
  {u'dateTime': u'2015-02-15', u'value': u'16044'},
  {u'dateTime': u'2015-02-16', u'value': u'4705'},
  {u'dateTime': u'2015-02-17', u'value': u'13'},
  {u'dateTime': u'2015-02-18', u'value': u'9061'},
  {u'dateTime': u'2015-02-19', u'value': u'11970'},
  {u'dateTime': u'2015-02-20', u'value': u'14401'},
  {u'dateTime': u'2015-02-21', u'value': u'8682'},
  {u'dateTime': u'2015-02-22', u'value': u'7323'},
  {u'dateTime': u'2015-02-23', u'value': u'6304'},
  {u'dateTime': u'2015-02-24', u'value': u'9246'},
  {u'dateTime': u'2015-02-25', u'value': u'7958'},
  {u'dateTime': u'2015-02-26', u'value': u'7417'},
  {u'dateTime': u'2015-02-27', u'value': u'17201'},
  {u'dateTime': u'2015-02-28', u'value': u'7985'},
  {u'dateTime': u'2015-03-01', u'value': u'4972'},
  {u'dateTime': u'2015-03-02', u'value': u'12488'},
  {u'dateTime': u'2015-03-03', u'value': u'8234'},
  {u'dateTime': u'2015-03-04', u'value': u'10550'},
  {u'dateTime': u'2015-03-05', u'value': u'11735'},
  {u'dateTime': u'2015-03-06', u'value': u'13726'},
  {u'dateTime': u'2015-03-07', u'value': u'8039'},
  {u'dateTime': u'2015-03-08', u'value': u'11697'},
  {u'dateTime': u'2015-03-09', u'value': u'14743'},
  {u'dateTime': u'2015-03-10', u'value': u'4231'},
  {u'dateTime': u'2015-03-11', u'value': u'10944'},
  {u'dateTime': u'2015-03-12', u'value': u'7463'},
  {u'dateTime': u'2015-03-13', u'value': u'16267'},
  {u'dateTime': u'2015-03-14', u'value': u'15152'},
  {u'dateTime': u'2015-03-15', u'value': u'17159'},
  {u'dateTime': u'2015-03-16', u'value': u'12973'},
  {u'dateTime': u'2015-03-17', u'value': u'6457'},
  {u'dateTime': u'2015-03-18', u'value': u'12681'},
  {u'dateTime': u'2015-03-19', u'value': u'19243'},
  {u'dateTime': u'2015-03-20', u'value': u'23615'},
  {u'dateTime': u'2015-03-21', u'value': u'8277'},
  {u'dateTime': u'2015-03-22', u'value': u'4540'},
  {u'dateTime': u'2015-03-23', u'value': u'10161'},
  {u'dateTime': u'2015-03-24', u'value': u'18766'},
  {u'dateTime': u'2015-03-25', u'value': u'9170'},
  {u'dateTime': u'2015-03-26', u'value': u'12516'},
  {u'dateTime': u'2015-03-27', u'value': u'15065'},
  {u'dateTime': u'2015-03-28', u'value': u'14415'},
  {u'dateTime': u'2015-03-29', u'value': u'10454'},
  {u'dateTime': u'2015-03-30', u'value': u'11572'},
  {u'dateTime': u'2015-03-31', u'value': u'10988'},
  {u'dateTime': u'2015-04-01', u'value': u'10335'},
  {u'dateTime': u'2015-04-02', u'value': u'10487'},
  {u'dateTime': u'2015-04-03', u'value': u'6455'},
  {u'dateTime': u'2015-04-04', u'value': u'11259'},
  {u'dateTime': u'2015-04-05', u'value': u'10492'},
  {u'dateTime': u'2015-04-06', u'value': u'6114'},
  {u'dateTime': u'2015-04-07', u'value': u'12085'},
  {u'dateTime': u'2015-04-08', u'value': u'10646'},
  {u'dateTime': u'2015-04-09', u'value': u'9343'},
  {u'dateTime': u'2015-04-10', u'value': u'12301'},
  {u'dateTime': u'2015-04-11', u'value': u'16441'},
  {u'dateTime': u'2015-04-12', u'value': u'16418'},
  {u'dateTime': u'2015-04-13', u'value': u'14180'},
  {u'dateTime': u'2015-04-14', u'value': u'13101'},
  {u'dateTime': u'2015-04-15', u'value': u'16287'},
  {u'dateTime': u'2015-04-16', u'value': u'13443'},
  {u'dateTime': u'2015-04-17', u'value': u'15173'},
  {u'dateTime': u'2015-04-18', u'value': u'17459'},
  {u'dateTime': u'2015-04-19', u'value': u'10720'},
  {u'dateTime': u'2015-04-20', u'value': u'14005'},
  {u'dateTime': u'2015-04-21', u'value': u'13524'},
  {u'dateTime': u'2015-04-22', u'value': u'11538'},
  {u'dateTime': u'2015-04-23', u'value': u'37'},
  {u'dateTime': u'2015-04-24', u'value': u'13672'},
  {u'dateTime': u'2015-04-25', u'value': u'7757'},
  {u'dateTime': u'2015-04-26', u'value': u'7809'},
  {u'dateTime': u'2015-04-27', u'value': u'14425'},
  {u'dateTime': u'2015-04-28', u'value': u'8800'},
  {u'dateTime': u'2015-04-29', u'value': u'13733'},
  {u'dateTime': u'2015-04-30', u'value': u'11194'},
  {u'dateTime': u'2015-05-01', u'value': u'4533'},
  {u'dateTime': u'2015-05-02', u'value': u'7717'},
  {u'dateTime': u'2015-05-03', u'value': u'10205'},
  {u'dateTime': u'2015-05-04', u'value': u'15252'},
  {u'dateTime': u'2015-05-05', u'value': u'11558'},
  {u'dateTime': u'2015-05-06', u'value': u'13332'},
  {u'dateTime': u'2015-05-07', u'value': u'9493'},
  {u'dateTime': u'2015-05-08', u'value': u'13623'},
  {u'dateTime': u'2015-05-09', u'value': u'10889'},
  {u'dateTime': u'2015-05-10', u'value': u'8745'},
  {u'dateTime': u'2015-05-11', u'value': u'11412'},
  {u'dateTime': u'2015-05-12', u'value': u'16062'},
  {u'dateTime': u'2015-05-13', u'value': u'16468'},
  {u'dateTime': u'2015-05-14', u'value': u'10317'},
  {u'dateTime': u'2015-05-15', u'value': u'16558'},
  {u'dateTime': u'2015-05-16', u'value': u'12482'},
  {u'dateTime': u'2015-05-17', u'value': u'19384'},
  {u'dateTime': u'2015-05-18', u'value': u'13640'},
  {u'dateTime': u'2015-05-19', u'value': u'10461'},
  {u'dateTime': u'2015-05-20', u'value': u'10556'},
  {u'dateTime': u'2015-05-21', u'value': u'21507'},
  {u'dateTime': u'2015-05-22', u'value': u'11873'},
  {u'dateTime': u'2015-05-23', u'value': u'9364'},
  {u'dateTime': u'2015-05-24', u'value': u'6935'},
  {u'dateTime': u'2015-05-25', u'value': u'5868'},
  {u'dateTime': u'2015-05-26', u'value': u'14429'},
  {u'dateTime': u'2015-05-27', u'value': u'10274'},
  {u'dateTime': u'2015-05-28', u'value': u'8654'},
  {u'dateTime': u'2015-05-29', u'value': u'15993'},
  {u'dateTime': u'2015-05-30', u'value': u'8559'},
  {u'dateTime': u'2015-05-31', u'value': u'19640'},
  {u'dateTime': u'2015-06-01', u'value': u'12937'},
  {u'dateTime': u'2015-06-02', u'value': u'15562'},
  {u'dateTime': u'2015-06-03', u'value': u'10618'},
  {u'dateTime': u'2015-06-04', u'value': u'10852'},
  {u'dateTime': u'2015-06-05', u'value': u'8866'},
  {u'dateTime': u'2015-06-06', u'value': u'14205'},
  {u'dateTime': u'2015-06-07', u'value': u'8475'},
  {u'dateTime': u'2015-06-08', u'value': u'19164'},
  {u'dateTime': u'2015-06-09', u'value': u'10276'},
  {u'dateTime': u'2015-06-10', u'value': u'13768'},
  {u'dateTime': u'2015-06-11', u'value': u'7187'},
  {u'dateTime': u'2015-06-12', u'value': u'16725'},
  {u'dateTime': u'2015-06-13', u'value': u'12203'},
  {u'dateTime': u'2015-06-14', u'value': u'13009'},
  {u'dateTime': u'2015-06-15', u'value': u'14168'},
  {u'dateTime': u'2015-06-16', u'value': u'12749'},
  {u'dateTime': u'2015-06-17', u'value': u'6956'},
  {u'dateTime': u'2015-06-18', u'value': u'26702'},
  {u'dateTime': u'2015-06-19', u'value': u'12047'},
  {u'dateTime': u'2015-06-20', u'value': u'15944'},
  {u'dateTime': u'2015-06-21', u'value': u'9838'},
  {u'dateTime': u'2015-06-22', u'value': u'12883'},
  {u'dateTime': u'2015-06-23', u'value': u'12967'},
  {u'dateTime': u'2015-06-24', u'value': u'23544'},
  {u'dateTime': u'2015-06-25', u'value': u'16666'},
  {u'dateTime': u'2015-06-26', u'value': u'9602'},
  {u'dateTime': u'2015-06-27', u'value': u'15613'},
  {u'dateTime': u'2015-06-28', u'value': u'4810'},
  {u'dateTime': u'2015-06-29', u'value': u'11100'},
  {u'dateTime': u'2015-06-30', u'value': u'15086'},
  {u'dateTime': u'2015-07-01', u'value': u'12265'},
  {u'dateTime': u'2015-07-02', u'value': u'17124'},
  {u'dateTime': u'2015-07-03', u'value': u'7883'},
  {u'dateTime': u'2015-07-04', u'value': u'12588'},
  {u'dateTime': u'2015-07-05', u'value': u'10071'},
  {u'dateTime': u'2015-07-06', u'value': u'15627'},
  {u'dateTime': u'2015-07-07', u'value': u'16888'},
  {u'dateTime': u'2015-07-08', u'value': u'16664'},
  {u'dateTime': u'2015-07-09', u'value': u'10598'},
  {u'dateTime': u'2015-07-10', u'value': u'14089'},
  {u'dateTime': u'2015-07-11', u'value': u'12786'},
  {u'dateTime': u'2015-07-12', u'value': u'7992'},
  {u'dateTime': u'2015-07-13', u'value': u'11561'},
  {u'dateTime': u'2015-07-14', u'value': u'13469'},
  {u'dateTime': u'2015-07-15', u'value': u'0'},
  {u'dateTime': u'2015-07-16', u'value': u'12774'},
  {u'dateTime': u'2015-07-17', u'value': u'11196'},
  {u'dateTime': u'2015-07-18', u'value': u'8555'},
  {u'dateTime': u'2015-07-19', u'value': u'9275'},
  {u'dateTime': u'2015-07-20', u'value': u'11809'},
  {u'dateTime': u'2015-07-21', u'value': u'11422'},
  {u'dateTime': u'2015-07-22', u'value': u'9391'},
  {u'dateTime': u'2015-07-23', u'value': u'12326'},
  {u'dateTime': u'2015-07-24', u'value': u'6549'},
  {u'dateTime': u'2015-07-25', u'value': u'5568'},
  {u'dateTime': u'2015-07-26', u'value': u'5790'},
  {u'dateTime': u'2015-07-27', u'value': u'9392'},
  {u'dateTime': u'2015-07-28', u'value': u'7615'},
  {u'dateTime': u'2015-07-29', u'value': u'30240'},
  {u'dateTime': u'2015-07-30', u'value': u'39697'},
  {u'dateTime': u'2015-07-31', u'value': u'27265'},
  {u'dateTime': u'2015-08-01', u'value': u'30887'},
  {u'dateTime': u'2015-08-02', u'value': u'8857'},
  {u'dateTime': u'2015-08-03', u'value': u'0'},
  {u'dateTime': u'2015-08-04', u'value': u'0'},
  {u'dateTime': u'2015-08-05', u'value': u'0'},
  {u'dateTime': u'2015-08-06', u'value': u'0'},
  {u'dateTime': u'2015-08-07', u'value': u'0'},
  {u'dateTime': u'2015-08-08', u'value': u'0'},
  {u'dateTime': u'2015-08-09', u'value': u'0'},
  {u'dateTime': u'2015-08-10', u'value': u'0'},
  {u'dateTime': u'2015-08-11', u'value': u'0'},
  {u'dateTime': u'2015-08-12', u'value': u'0'},
  {u'dateTime': u'2015-08-13', u'value': u'803'},
  {u'dateTime': u'2015-08-14', u'value': u'12944'},
  {u'dateTime': u'2015-08-15', u'value': u'7002'},
  {u'dateTime': u'2015-08-16', u'value': u'0'},
  {u'dateTime': u'2015-08-17', u'value': u'0'},
  {u'dateTime': u'2015-08-18', u'value': u'0'},
  {u'dateTime': u'2015-08-19', u'value': u'0'},
  {u'dateTime': u'2015-08-20', u'value': u'0'},
  {u'dateTime': u'2015-08-21', u'value': u'0'}]}

Umwandeln in eine Pandas Series


In [37]:
steps_dict = {}
for step in activities_steps['activities-steps']:
    steps_dict[step['dateTime']] = int(step['value'])

In [39]:
from pandas import Series
steps = Series(steps_dict, name='Steps')

In [59]:
steps.head()


Out[59]:
2014-08-22    12673
2014-08-23    11408
2014-08-24    11585
2014-08-25    16252
2014-08-26    12152
Name: Steps, dtype: int64

Aufräumen der Daten

  • Tage mit Null Schritten entfernen; dort war der Akku leer o.ä.
  • Null Schritte werden ersetzt durch NaN ("Not a Number")
  • Numpy: nan
  • Bemerkung: Man könnte auch weiter gehen und z.B. Tage mit weniger als n Schritten entfernen

In [65]:
import numpy as np
cleaned_steps = steps.replace(0, np.nan)

In [66]:
cleaned_steps.tail()


Out[66]:
2015-08-17   NaN
2015-08-18   NaN
2015-08-19   NaN
2015-08-20   NaN
2015-08-21   NaN
Name: Steps, dtype: float64

Tage mit NaN (Null Schritte) wegwerfen

  • Pandas-Funktion dropna

In [68]:
cleaned_steps = cleaned_steps.dropna()

In [69]:
cleaned_steps.tail()


Out[69]:
2015-08-01    30887
2015-08-02     8857
2015-08-13      803
2015-08-14    12944
2015-08-15     7002
Name: Steps, dtype: float64

Die Schritte als Grafik


In [70]:
import matplotlib.pyplot as plt
plt.figure()
cleaned_steps.plot(label='Steps')
plt.legend(loc='best')


Out[70]:
<matplotlib.legend.Legend at 0x110309e50>

Die Verteilung der Schritte

  • Histogramm-Funktion von Pandas

In [74]:
cleaned_steps.hist(bins=10, color='darkseagreen')
plt.suptitle('Daily steps distribution (last year); 10 bins')


Out[74]:
<matplotlib.text.Text at 0x110fdb710>

In [78]:
cleaned_steps.hist(bins=100, color='darkseagreen')
plt.suptitle('Daily steps distribution (last year); 100 bins')


Out[78]:
<matplotlib.text.Text at 0x111da1650>

Vergleich zweier Schrittzähler

Fitbit One vs. Microsoft Band

Daten vom Microsoft Band holen

  • Der Einfachheit halber exportieren wir Daten aus Microsoft Health

Microsoft Health Dashboard

Export der Daten aus Microsoft Health

Einlesen der exportierten Daten

  • Einlesen in ein Pandas DataFrame
  • Bequeme Funktion read_csv

In [84]:
band_daily_summary = pd.read_csv('data/Microsoft_Health_20140821_20150821.csv', index_col='Date')

In [87]:
band_daily_summary.tail()


Out[87]:
Steps Calories HR_Lowest HR_Highest HR_Average Total_Kilometers_Moved Active_Hours Total_Seconds_All_Activities Total_Calories_All_Activities Sleep_Events ... Exercise_Events Exercise_Total_Seconds Exercise_Total_Calories Guided_Workout_Events Guided_Workout_Total_Seconds Guided_Workout_Total_Calories Golf_Events Golf_Total_Seconds Total_Kilometers_Golfed Golf_Total_Calories
Date
2015-08-17 12402 2199 62 112 74 7.85174 6 NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2015-08-18 8844 2119 62 120 76 5.52216 3 NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2015-08-19 7862 2062 60 109 73 4.89936 6 NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2015-08-20 8486 2106 61 100 75 5.24184 4 NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2015-08-21 10802 2030 54 102 71 6.73544 8 NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

5 rows × 30 columns

Aufräumen der Daten

  • Uns interessieren hier nur die Schritte; alles andere wegschmeissen
  • Spalte mit den Schritten umbenennen

In [92]:
band_steps_summary = band_daily_summary['Steps']
band_steps = pd.DataFrame(band_steps_summary)
band_steps = band_steps.rename(columns = {'Steps':'Steps Microsoft Band'})

In [91]:
band_steps.tail()


Out[91]:
Steps Microsoft Band
Date
2015-08-17 12402
2015-08-18 8844
2015-08-19 7862
2015-08-20 8486
2015-08-21 10802

In [93]:
band_steps.plot()


Out[93]:
<matplotlib.axes._subplots.AxesSubplot at 0x112ea49d0>

Vorbereitung des Vergleichs

Daten von Fitbit in die gleiche Form bringen


In [97]:
fitbit_steps = pd.DataFrame(cleaned_steps)
#df_steps_fitbit.rename(index={0:'Date'})
fitbit_steps.index.names = ['Date']
fitbit_steps = fitbit_steps.rename(columns = {'Steps':'Steps Fitbit'})

In [99]:
fitbit_steps.tail()


Out[99]:
Steps Fitbit
Date
2015-08-01 30887
2015-08-02 8857
2015-08-13 803
2015-08-14 12944
2015-08-15 7002

Beide Tabellen in einer Tabelle (DataFrame) vereinen


In [101]:
steps_combined = pd.DataFrame.join(band_steps, fitbit_steps)

In [103]:
steps_combined.tail()


Out[103]:
Steps Microsoft Band Steps Fitbit
Date
2015-08-17 12402 NaN
2015-08-18 8844 NaN
2015-08-19 7862 NaN
2015-08-20 8486 NaN
2015-08-21 10802 NaN

In [104]:
steps_combined.plot()


Out[104]:
<matplotlib.axes._subplots.AxesSubplot at 0x1132474d0>

Differenz der Schritte berechnen

  • Wir erstellen eine Funktion zum Berechnen der Differenz
  • Differenzen größer als 900 Schritte werden verworfern (Rückgabe None)
  • Warum 900? Bauchgefühl; irgendwas stimmt nicht ;)

In [105]:
def differences(series):
    diff = series['Steps Microsoft Band'] - series['Steps Fitbit']
    if abs(diff) > 900:
        return None
    else:
        return diff

Neue Spalte mit Schrittdifferenz zur Tabelle (DataFrame) hinzufügen


In [106]:
steps_combined['Difference'] = steps_combined.apply(differences, axis=1)

In [108]:
steps_combined['Difference'].hist(bins=10)
plt.title("Microsoft Band vs. Fitbit One: Deviation of daily steps")
plt.ylabel('count')
plt.xlabel('Steps deviation compared with Fitbit One');


Besser als Vergleich: Prozentuale Abweichung

  • Definition einer Funktion zum Ausrechnen der prozentualen Abweichung

In [109]:
def percentage(series):
    percent = 100.-100.*min(series['Steps Microsoft Band'], series['Steps Fitbit'])/max(series['Steps Microsoft Band'], series['Steps Fitbit'])
    if percent > 15.:
        return None
    else:
        return percent

Neue Spalte mit prozentualer Abweichung zur Tabelle (DataFrame) hinzufügen


In [110]:
steps_combined['Percentage'] = steps_combined.apply(percentage, axis=1)

In [115]:
steps_combined['Percentage'].hist(bins=20)
plt.title("Microsoft Band vs. Fitbit One: Deviation of daily steps")
plt.ylabel('count')
plt.xlabel('Steps deviation (%) compared with Fitbit One');



In [114]:
steps_combined


Out[114]:
Steps Microsoft Band Steps Fitbit Difference Percentage
Date
2014-08-21 0 NaN NaN NaN
2014-08-22 0 12673 NaN NaN
2014-08-23 0 11408 NaN NaN
2014-08-24 0 11585 NaN NaN
2014-08-25 0 16252 NaN NaN
2014-08-26 0 12152 NaN NaN
2014-08-27 0 6309 NaN NaN
2014-08-28 0 10156 NaN NaN
2014-08-29 0 8732 NaN NaN
2014-08-30 0 10810 NaN NaN
2014-08-31 0 3325 NaN NaN
2014-09-01 0 11830 NaN NaN
2014-09-02 0 7 -7 NaN
2014-09-03 0 9995 NaN NaN
2014-09-04 0 6651 NaN NaN
2014-09-05 0 10855 NaN NaN
2014-09-06 0 6759 NaN NaN
2014-09-07 0 11321 NaN NaN
2014-09-08 0 13040 NaN NaN
2014-09-09 0 8958 NaN NaN
2014-09-10 0 11548 NaN NaN
2014-09-11 0 10912 NaN NaN
2014-09-12 0 11327 NaN NaN
2014-09-13 0 7225 NaN NaN
2014-09-14 0 4398 NaN NaN
2014-09-15 0 NaN NaN NaN
2014-09-16 0 10849 NaN NaN
2014-09-17 0 11318 NaN NaN
2014-09-18 0 13524 NaN NaN
2014-09-19 0 8781 NaN NaN
... ... ... ... ...
2015-07-23 11384 12326 NaN 7.642382
2015-07-24 5194 6549 NaN NaN
2015-07-25 4200 5568 NaN NaN
2015-07-26 4740 5790 NaN NaN
2015-07-27 8452 9392 NaN 10.008518
2015-07-28 6278 7615 NaN NaN
2015-07-29 24580 30240 NaN NaN
2015-07-30 38608 39697 NaN 2.743280
2015-07-31 25672 27265 NaN 5.842655
2015-08-01 28422 30887 NaN 7.980704
2015-08-02 7810 8857 NaN 11.821158
2015-08-03 3836 NaN NaN 0.000000
2015-08-04 7128 NaN NaN 0.000000
2015-08-05 0 NaN NaN NaN
2015-08-06 7780 NaN NaN 0.000000
2015-08-07 9748 NaN NaN 0.000000
2015-08-08 280 NaN NaN 0.000000
2015-08-09 0 NaN NaN NaN
2015-08-10 0 NaN NaN NaN
2015-08-11 7016 NaN NaN 0.000000
2015-08-12 8346 NaN NaN 0.000000
2015-08-13 15020 803 NaN NaN
2015-08-14 12308 12944 -636 4.913473
2015-08-15 5726 7002 NaN NaN
2015-08-16 4548 NaN NaN 0.000000
2015-08-17 12402 NaN NaN 0.000000
2015-08-18 8844 NaN NaN 0.000000
2015-08-19 7862 NaN NaN 0.000000
2015-08-20 8486 NaN NaN 0.000000
2015-08-21 10802 NaN NaN 0.000000

366 rows × 4 columns

Muse Headband EEG

Muse - The Brainsensing Headband

MuseIO starten

  • MuseIO empfängt Daten per Bluetooth und stellt sie per OSC-Protokoll zur Verfügung

  • In das MuseIO-Installationsverzeichnis wechseln

    $ cd ./to/your/muse_io_path
  • MuseIO starten

    $ muse-io --osc osc.udp://localhost:5008

Bokeh

  • Mal nicht Matplotlib zur Visualisierung, sondern Bokeh
  • Bokeh ist eine web-basierte Visualisierungbibliothek
  • http://bokeh.pydata.org

Bokeh Server starten

$ bokeh-server

Bokeh importieren


In [116]:
from bokeh.plotting import figure, output_server, output_notebook, cursession, show

Daten vom Muse empfangen

Vorbereitungen


In [117]:
from liblo import *
from thread import start_new_thread

In [119]:
# Where to listen for received OSC messages by Muse IO
MUSE_IO_PORT = 5008
# Values to display (x-axis)
DISPLAY_RANGE = 1200

delta0 = 0
delta1 = 2500
delta2 = 5000
delta3 = 7500

transmission_running = False

Bokeh vorbereiten


In [120]:
output_server("mindwaves")
output_notebook()

TOOLS = "wheel_zoom, box_zoom, reset, resize"

f = figure(background_fill = "#222222", tools = TOOLS)

# prevent rendering axis labels
f.xaxis.bounds = [0,0]
f.yaxis.bounds = [0,0]

# lines with DISPLAY_RANGE elements; set values to delta[0-3]
f.line(range(DISPLAY_RANGE), [delta3] * DISPLAY_RANGE, name = 'mind_wave', color = "#ef002a")
f.line(range(DISPLAY_RANGE), [delta2] * DISPLAY_RANGE, name = 'mind_wave', color = "#ff9400")
f.line(range(DISPLAY_RANGE), [delta1] * DISPLAY_RANGE, name = 'mind_wave', color = "#0b5fa5")
f.line(range(DISPLAY_RANGE), [delta0] * DISPLAY_RANGE, name = 'mind_wave', color = "#41db00")


Using saved session configuration for http://localhost:5006/
To override, pass 'load_from_config=False' to Session
BokehJS successfully loaded.
Out[120]:
<bokeh.plotting.Figure at 0x114521310>

Lesbare Variablen für Graphen setzen


In [121]:
renderer = f.select(dict(name="mind_wave"))

l_ear_ds = renderer[0].data_source
l_forehead_ds = renderer[1].data_source
r_forehead_ds = renderer[2].data_source
r_ear_ds = renderer[3].data_source

Muse Server mit Callback


In [122]:
class MuseServer(ServerThread):
    
    # listen for messages on MUSE_IO_PORT
    def __init__(self):
        ServerThread.__init__(self, MUSE_IO_PORT)
    
    # receive EEG data
    @make_method('/muse/eeg', 'ffff')
    def eeg_callback(self, path, args):
        global l_ear_ds, l_forehead_ds, r_forehead_ds, r_ear_ds, transmission_running
        
        l_ear, l_forehead, r_forehead, r_ear = args
        
        l_ear_ds.data["y"].append(l_ear + delta3)
        l_forehead_ds.data["y"].append(l_forehead + delta2)
        r_forehead_ds.data["y"].append(r_forehead + delta1)
        r_ear_ds.data["y"].append(r_ear + delta0) 
        
        # send data to bokeh-server only if there is no transmission running
        if (transmission_running == False):
            transmission_running = True
            start_new_thread(start_transmission_process, ())

Parellele Datenübertragung für bessere Performance


In [124]:
def start_transmission_process():
    global l_ear_ds, l_forehead_ds, r_forehead_ds, r_ear_ds, transmission_running
    
    # only keep "some" (DISPLAY_RANGE) values
    l_ear_ds.data["y"][0:-DISPLAY_RANGE] = []  
    l_forehead_ds.data["y"][0:-DISPLAY_RANGE] = []  
    r_forehead_ds.data["y"][0:-DISPLAY_RANGE] = []  
    r_ear_ds.data["y"][0:-DISPLAY_RANGE] = [] 
    
    # send data sources to bokeh-server
    cursession().store_objects(l_ear_ds, l_forehead_ds, r_forehead_ds, r_ear_ds)
    
    # finished transmission
    transmission_running = False

Muse Server instanziieren


In [147]:
server = MuseServer()


server error 9904: cannot find free port

Bokeh Plot anzeigen und Server starten


In [145]:
show(f)
server.start()



In [146]:
server.stop()

Vielen Dank!

Wer sich für mehr interessiert...


In [2]:
% pylab inline


Populating the interactive namespace from numpy and matplotlib